// Copyright (c) 2025 Huawei Technologies Co., Ltd.
// openFuyao is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//
//	http://license.coscl.org.cn/MulanPSL2
//
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.

package lm

import (
	"errors"
	"fmt"
	"reflect"
	"testing"

	"github.com/agiledragon/gomonkey/v2"
	"github.com/urfave/cli/v2"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/kubernetes/fake"

	kubernetes2 "openfuyao.com/npu-feature-discovery/internal/kubernetes"
	"openfuyao.com/npu-feature-discovery/internal/utils"
)

func TestNewAscendLabelerCannotGetNode(t *testing.T) {
	patch := gomonkey.ApplyFunc(getNode, func(c *cli.Context) (kubernetes.Interface, *corev1.Node, error) {
		return nil, nil, fmt.Errorf("failed to get Kubernetes client")
	})
	defer patch.Reset()
	type args struct {
		c *cli.Context
	}
	tests := []struct {
		name                 string
		argsCannotGetNode    args
		wantCannotGetNode    Labeler
		wantErrCannotGetNode bool
	}{
		{
			name: "get node failed",
			argsCannotGetNode: args{
				c: nil,
			},
			wantCannotGetNode:    nil,
			wantErrCannotGetNode: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := newAscendLabeler(tt.argsCannotGetNode.c)
			if (err != nil) != tt.wantErrCannotGetNode {
				t.Errorf("newAscendLabeler() error = %v, wantErr %v", err, tt.wantErrCannotGetNode)
				return
			}
			if !reflect.DeepEqual(got, tt.wantCannotGetNode) {
				t.Errorf("newAscendLabeler() = %v, want %v", got, tt.wantErrCannotGetNode)
			}
		})
	}
}

func TestNewAscendLabelerCannotGetRuntimeLabeler(t *testing.T) {
	node := &corev1.Node{}
	patch0 := gomonkey.ApplyFunc(getNode, func(c *cli.Context) (kubernetes.Interface, *corev1.Node, error) {
		return fake.NewSimpleClientset(node), node, nil
	})
	patch1 := gomonkey.ApplyFunc(newRuntimeLabeler, func(node *corev1.Node) (Labeler, error) {
		return nil, fmt.Errorf("unsupported CRI runtime")
	})
	defer patch0.Reset()
	defer patch1.Reset()
	type args struct {
		c *cli.Context
	}
	tests := []struct {
		name                           string
		argsCannotGetRuntimeLabeler    args
		wantCannotGetRuntimeLabeler    Labeler
		wantErrCannotGetRuntimeLabeler bool
	}{
		{
			name: "get runtime failed",
			argsCannotGetRuntimeLabeler: args{
				c: nil,
			},
			wantCannotGetRuntimeLabeler:    nil,
			wantErrCannotGetRuntimeLabeler: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := newAscendLabeler(tt.argsCannotGetRuntimeLabeler.c)
			if (err != nil) != tt.wantErrCannotGetRuntimeLabeler {
				t.Errorf("newAscendLabeler() error = %v, wantErr %v", err, tt.wantErrCannotGetRuntimeLabeler)
				return
			}
			if !reflect.DeepEqual(got, tt.wantCannotGetRuntimeLabeler) {
				t.Errorf("newAscendLabeler() = %v, want %v", got, tt.wantCannotGetRuntimeLabeler)
			}
		})
	}
}

func TestGetNodeNoK8s(t *testing.T) {
	patch := gomonkey.ApplyFunc(kubernetes2.GetKubernetesClient,
		func() (kubernetes.Interface, error) { return nil, errors.New(`kubernetes does not exist`) })
	defer patch.Reset()
	type args struct {
		c *cli.Context
	}
	tests := []struct {
		name                string
		argsGetNodeNoK8s    args
		want0GetNodeNoK8s   kubernetes.Interface
		want1GetNodeNoK8s   *corev1.Node
		wantErrGetNodeNoK8s bool
	}{
		{
			name:                "no k8s error",
			argsGetNodeNoK8s:    args{},
			want0GetNodeNoK8s:   nil,
			want1GetNodeNoK8s:   nil,
			wantErrGetNodeNoK8s: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, got1, err := getNode(tt.argsGetNodeNoK8s.c)
			if (err != nil) != tt.wantErrGetNodeNoK8s {
				t.Errorf("getNode() error = %v, wantErr %v", err, tt.wantErrGetNodeNoK8s)
				return
			}
			if !reflect.DeepEqual(got, tt.want0GetNodeNoK8s) {
				t.Errorf("getNode() got = %v, want %v", got, tt.want0GetNodeNoK8s)
			}
			if !reflect.DeepEqual(got1, tt.want1GetNodeNoK8s) {
				t.Errorf("getNode() got1 = %v, want %v", got1, tt.want1GetNodeNoK8s)
			}
		})
	}
}

func TestGetNodeNoEnv(t *testing.T) {
	patch0 := gomonkey.ApplyFunc(kubernetes2.GetKubernetesClient,
		func() (kubernetes.Interface, error) { return &fake.Clientset{}, nil })
	defer patch0.Reset()
	patch1 := gomonkey.ApplyFunc(utils.GetEnvVar,
		func(_ string) (string, error) { return "", errors.New("env var not found") })
	defer patch1.Reset()
	type args struct {
		c *cli.Context
	}
	tests := []struct {
		name                string
		argsGetNodeNoEnv    args
		want0GetNodeNoEnv   kubernetes.Interface
		want1GetNodeNoEnv   *corev1.Node
		wantErrGetNodeNoEnv bool
	}{
		{
			name:                "no env error",
			argsGetNodeNoEnv:    args{},
			want0GetNodeNoEnv:   nil,
			want1GetNodeNoEnv:   nil,
			wantErrGetNodeNoEnv: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, got1, err := getNode(tt.argsGetNodeNoEnv.c)
			if (err != nil) != tt.wantErrGetNodeNoEnv {
				t.Errorf("getNode() error = %v, wantErr %v", err, tt.wantErrGetNodeNoEnv)
				return
			}
			if !reflect.DeepEqual(got, tt.want0GetNodeNoEnv) {
				t.Errorf("getNode() got = %v, want %v", got, tt.want0GetNodeNoEnv)
			}
			if !reflect.DeepEqual(got1, tt.want1GetNodeNoEnv) {
				t.Errorf("getNode() got1 = %v, want %v", got1, tt.want1GetNodeNoEnv)
			}
		})
	}
}

func TestGetNodFail(t *testing.T) {
	nodeName := `test-node`
	fakeClient := fake.NewSimpleClientset(&corev1.Node{})
	patch0 := gomonkey.ApplyFunc(kubernetes2.GetKubernetesClient,
		func() (kubernetes.Interface, error) { return fakeClient, nil })
	defer patch0.Reset()
	patch1 := gomonkey.ApplyFunc(utils.GetEnvVar,
		func(_ string) (string, error) { return nodeName, nil })
	defer patch1.Reset()
	type args struct {
		ctxGetNodFail *cli.Context
	}
	tests := []struct {
		name              string
		argsGetNodFail    args
		want0GetNodFail   kubernetes.Interface
		want1GetNodFail   *corev1.Node
		wantErrGetNodFail bool
	}{
		{
			name:              "get node fail",
			argsGetNodFail:    args{&cli.Context{}},
			want0GetNodFail:   nil,
			want1GetNodFail:   nil,
			wantErrGetNodFail: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, got1, err := getNode(tt.argsGetNodFail.ctxGetNodFail)
			if (err != nil) != tt.wantErrGetNodFail {
				t.Errorf("getNode() error = %v, wantErr %v", err, tt.wantErrGetNodFail)
				return
			}
			if !reflect.DeepEqual(got, tt.want0GetNodFail) {
				t.Errorf("getNode() got = %v, want %v", got, tt.want0GetNodFail)
			}
			if !reflect.DeepEqual(got1, tt.want1GetNodFail) {
				t.Errorf("getNode() got1 = %v, want %v", got1, tt.want1GetNodFail)
			}
		})
	}
}

func TestGetNodeSuccess(t *testing.T) {
	nodeName := `test-node`
	fakeClient := fake.NewSimpleClientset(&corev1.Node{
		ObjectMeta: metav1.ObjectMeta{
			Name: nodeName,
		},
	})
	patch0 := gomonkey.ApplyFunc(utils.GetEnvVar,
		func(_ string) (string, error) { return nodeName, nil })
	defer patch0.Reset()
	patch1 := gomonkey.ApplyFunc(kubernetes2.GetKubernetesClient,
		func() (kubernetes.Interface, error) { return fakeClient, nil })
	defer patch1.Reset()
	type args struct {
		ctxGetNodeSuccess *cli.Context
	}
	tests := []struct {
		name                  string
		argsGetNodeSuccess    args
		want0GetNodeSuccess   kubernetes.Interface
		want1GetNodeSuccess   *corev1.Node
		wantErrGetNodeSuccess bool
	}{
		{
			name:                "success",
			argsGetNodeSuccess:  args{&cli.Context{}},
			want0GetNodeSuccess: fakeClient,
			want1GetNodeSuccess: &corev1.Node{
				ObjectMeta: metav1.ObjectMeta{
					Name: nodeName,
				},
			},
			wantErrGetNodeSuccess: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, got1, err := getNode(tt.argsGetNodeSuccess.ctxGetNodeSuccess)
			if (err != nil) != tt.wantErrGetNodeSuccess {
				t.Errorf("getNode() error = %v, wantErr %v", err, tt.wantErrGetNodeSuccess)
				return
			}
			if !reflect.DeepEqual(got, tt.want0GetNodeSuccess) {
				t.Errorf("getNode() got = %v, want %v", got, tt.want0GetNodeSuccess)
			}
			if !reflect.DeepEqual(got1, tt.want1GetNodeSuccess) {
				t.Errorf("getNode() got1 = %v, want %v", got1, tt.want1GetNodeSuccess)
			}
		})
	}
}
